home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- **
- ** Program: DTS.Lib
- ** File: ctlhandler.c
- ** Written by: Eric Soldan
- **
- ** Copyright © 1991 Apple Computer, Inc.
- ** All rights reserved.
- */
-
- /* This code implements the new 7.0 human-interface standards for both
- ** TextEdit and List controls. These standards include the following features:
- **
- ** 1) Tabbing between TextEdit and List controls within a window.
- ** 2) Displaying what item is active. The active TextEdit item is indicated
- ** by either a blinking caret, or a selection range.
- ** 3) List positioning via the keyboard. Entries on the keyboard automatically
- ** select and display the closest List item. Also, the up and down arrows
- ** scroll through the list.
- ** 4) Window scrollbars are handled.
- */
-
-
-
- /*****************************************************************************/
-
-
-
- #include "DTS.Lib2.h"
- #include "DTS.Lib.protos.h"
-
- #include "ListControlProcs.h"
- #include "TextEditControlProcs.h"
- #include "Utilities.h"
-
- static short HandleScrollEvent(WindowPtr window, EventRecord *event);
-
-
-
- /*****************************************************************************/
-
-
-
- static FileRecHndl gFrHndl;
- static Rect gScrollRct;
- static Point gKeepOrg;
- static Boolean gVert;
- extern short gBeginUpdateNested;
-
- static pascal void ScrollActionProc(ControlHandle scrollCtl, short part);
-
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- /* This function converts a control number to a control handle. The function
- ** simply walks the window's control list counting down until it has reached
- ** the right control number. It also returns the number of controls traversed.
- ** While often this will be the same as the control number passed in, if the
- ** number passed in is greater than the number of controls in the list, then
- ** the number returned is the number of controls in the list. */
-
- #pragma segment Controls
- short CNum2Ctl(WindowPtr window, short ctlNum, ControlHandle *ctl)
- {
- short numCtls;
-
- *ctl = nil;
- if (!ctlNum) return(0);
-
- *ctl = ((WindowPeek)window)->controlList;
- for (numCtls = 1; --ctlNum; ++numCtls) {
- if (!*ctl) break;
- *ctl = (**ctl)->nextControl;
- }
-
- return(numCtls);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function converts a control handle to a control number. The function
- ** simply walks the window's control list and counts how many controls it
- ** has to traverse before finding the target control. The smallest control
- ** number that can be returned is 1. This makes control numbers similar to
- ** dialog item numbers. */
-
- #pragma segment Controls
- short Ctl2CNum(ControlHandle ctl)
- {
- ControlHandle nextCtl;
- short ctlNum;
-
- if (!ctl) return(0);
-
- nextCtl = ((WindowPeek)(*ctl)->contrlOwner)->controlList;
- for (ctlNum = 0;;) {
- if (!nextCtl) break;
- ++ctlNum;
- if (ctl == nextCtl) break;
- nextCtl = (*nextCtl)->nextControl;
- }
- return(ctlNum);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function reactivates the last active TextEdit or List control for
- ** the indicated window. */
-
- #pragma segment Controls
- void DoCtlActivate(WindowPtr window)
- {
- BeginContent(window);
-
- if ((*gclFindActive)(window)) /* If the last item active was */
- (*gclWindActivate)(window); /* a List, then reactivate it. */
- else
- (*gcteWindActivate)(window); /* Otherwise, reactivate last TextEdit control. */
-
- EndContent(window);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function returns all the checkBox values into the dsignated array.
- ** The function walks the control list, and when it finds a checkBox control,
- ** it gets the control value and places it in the next position in the array.
- ** This allows a single call to retrieve all the checkBox values at once. */
-
- #pragma segment Controls
- void GetCheckBoxValues(WindowPtr window, Boolean checkBoxVal[])
- {
- ControlHandle nextCtl;
- short checkBoxIndx;
-
- nextCtl = ((WindowPeek)window)->controlList;
- for (checkBoxIndx = 0;;) {
- if (!nextCtl) return;
- if (GetButtonVariant(nextCtl) == checkBoxProc)
- checkBoxVal[checkBoxIndx++] = GetCtlValue(nextCtl);
- nextCtl = (*nextCtl)->nextControl;
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function returns which radio button is selected for a particular
- ** family of radio buttons. It finds the radio button of the target family
- ** with the lowest control number and it subtracts this number from the
- ** selected radio button of the same family. This gives a relative radio
- ** button number as a return result. This way the position of the family
- ** of radio buttons can change in the window and the return result is the
- ** same. The family number is stored in the control's refCon field. */
-
- #pragma segment Controls
- short GetRadioButtonChoice(WindowPtr window, short famNum)
- {
- ControlHandle nextCtl;
- short ctlNum, firstInFam;
-
- nextCtl = ((WindowPeek)window)->controlList;
- for (ctlNum = 0, firstInFam = -1;;) {
- if (!nextCtl) return(-1); /* If a proper radio button family was
- ** passed in, this can't happen. The
- ** -1 is an error that indicates that
- ** the requested family didn't exist,
- ** or that there was no selected
- ** radio of the requested family. */
- ++ctlNum;
- if (GetButtonVariant(nextCtl) == radioButProc) {
- if (GetCRefCon(nextCtl) == famNum) {
- if (firstInFam == -1)
- firstInFam = ctlNum;
- if (GetCtlValue(nextCtl)) return(ctlNum - firstInFam);
- }
- }
- nextCtl = (*nextCtl)->nextControl;
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function currently handles events for TextEdit, List, and button
- ** controls in a window. It also handles the window scrollbars and
- ** scrolling of the window. */
-
- #pragma segment Controls
- short IsCtlEvent(WindowPtr window, EventRecord *event, ControlHandle *retCtl, short *retAction)
- {
- RgnHandle frameRgn;
- TEHandle te, teNext;
- ListHandle list, listNext;
- ControlHandle ctl, activeCtl, teCtl, listCtl, nextCtl;
- short what, ctlNum, key, modifiers, mode, action;
- Boolean hitFrame, hitScrollBar;
- Point clickLoc;
- CTEDataHndl teData;
- CLDataHndl listData;
-
- action = 0;
-
- if (retCtl)
- *retCtl = nil;
- if (retAction)
- *retAction = 0;
-
- if ((what = event->what) == mouseDown) {
-
- frameRgn = DoCalcScrollRgn(window);
- hitFrame = PtInRgn(event->where, frameRgn);
- DisposeRgn(frameRgn);
- if (hitFrame) return(HandleScrollEvent(window, event));
- /* If window scrollbar is clicked on, handle the window scroll event. */
-
- BeginContent(window);
- clickLoc = event->where;
- GlobalToLocal(&clickLoc);
-
- (*gcteCtlHit)(); /* Clear CTECtl defProc's last hit CTECtl. */
- (*gclCtlHit)(); /* Clear CLCtl defProc's last hit CLCtl. */
-
- if (WhichControl(clickLoc, window, &ctl)) {
- /* WhichControl also finds inactive controls. We need this for scrollbars.
- ** If an inactive scrollbar is hit, we should activate the related
- ** TextEdit or List control. */
-
- hitScrollBar = IsScrollBar(ctl);
- /* The List controls and TextEdit controls may have scrollbars.
- ** Find out if a scrollbar was pressed, because it may belong
- ** to a TextEdit or List control. */
-
- FindControl(clickLoc, window, &ctl);
- /* WhichControl doesn't call the scrollProc. FindControl does.
- ** We need it called so we can determine below if a TextEdit or
- ** List control was hit. CTECtlHit() and CLCtlHit() return
- ** the last hit respective control. */
-
- ctlNum = Ctl2CNum(ctl);
-
- if ((hitScrollBar) || ((*gcteCtlHit)())) {
- /* This test is for speed. CTEClick would find out if a TextEdit
- ** control handled the mouse click, but not as fast as we would
- ** like. The above test determines if it is worth investigating. */
-
- if ((*gcteClick)(window, event, &action)) { /* If click is for TextEdit control... */
- if (list = (*gclFindActive)(window))
- (*gclActivate)(false, list);
- /* If old active control was a List control, deactivate it.
- ** The TextEdit control doesn't know how to do this. */
- if (action == -1) {
- teCtl = (*gcteViewFromTE)(CTEFindActive(window));
- teData = (CTEDataHndl)(*teCtl)->contrlData;
- mode = (*teData)->mode;
- if (!(mode & cteTwoStep))
- (*gcteClick)(window, event, &action);
- }
- EndContent(window);
- if (retCtl)
- *retCtl = ctl; /* Return the TextEdit control. */
- if (retAction)
- *retAction = action; /* Return action taken. */
- return(ctlNum);
- }
- }
-
- if ((hitScrollBar) || ((*gclCtlHit)())) {
- /* This test is for speed. CLClick would find out if a List
- ** control handled the mouse click, but not as fast as we would
- ** like. The above test determines if it is worth investigating. */
-
- if ((*gclClick)(window, event, &action)) { /* If click is for List control... */
- if (te = (*gcteFindActive)(window))
- (*gcteActivate)(false, te);
- /* If old active control was a TextEdit control, deactivate it.
- ** The List control doesn't know how to do this. */
- if (action == -1) { /* Just activated a List control... */
- listCtl = (*gclViewFromList)((*gclFindActive)(window));
- listData = (CLDataHndl)(*listCtl)->contrlData;
- mode = (*listData)->mode;
- if (!(mode & clTwoStep))
- (*gclClick)(window, event, &action);
- }
- EndContent(window);
- if (retCtl)
- *retCtl = ctl; /* Return the List control. */
- if (retAction)
- *retAction = action; /* Return action taken. */
- return(ctlNum);
- }
- }
-
- action = 0;
- if (!ctl) {
- EndContent(window);
- if (retCtl)
- *retCtl = ctl;
- if (retAction)
- *retAction = action;
- return(ctlNum);
- }
-
- if (TrackControl(ctl, clickLoc, (ProcPtr)-1)) { /* Handle other controls. */
-
- switch(GetButtonVariant(ctl)) {
- case pushButProc:
- break;
- case checkBoxProc:
- SetCtlValue(ctl, GetCtlValue(ctl) ^ 1); /* Toggle checkBox value. */
- break;
- case radioButProc:
- nextCtl = ((WindowPeek)window)->controlList;
- /* The below loop walks the control list for the window and
- ** finds radio buttons of the correct family number. If
- ** the found radio button is the one that was clicked on,
- ** the value is set true, otherwise it is set false. */
- for (; nextCtl; nextCtl = (*nextCtl)->nextControl) {
- if (GetButtonVariant(nextCtl) == radioButProc)
- if (GetCRefCon(nextCtl) == GetCRefCon(ctl))
- SetCtlValue(nextCtl, (nextCtl == ctl));
- }
- break;
- }
- }
- else {
- ctl = nil;
- ctlNum = 0;
- }
-
- EndContent(window);
- if (retCtl)
- *retCtl = ctl;
- if (retAction)
- *retAction = action;
- return(ctlNum);
- }
-
- EndContent(window);
- return(0);
- }
-
- if ((what == keyDown) || (what == autoKey)) { /* If event was keypress... */
-
- modifiers = event->modifiers;
- if (modifiers & cmdKey) return(0);
- /* Not our job to handle this one. */
-
- key = event->message & charCodeMask;
-
- if (key == chTab) { /* If tab... */
-
- teNext = nil;
- listNext = nil;
- activeCtl = nil;
-
- if (te = (*gcteFindActive)(window))
- activeCtl = (*gcteViewFromTE)(te);
- if (list = (*gclFindActive)(window))
- activeCtl = (*gclViewFromList)(list);
- /* Find what the active control is. */
-
- if (!(teCtl = (*gcteNext)(window, &teNext, activeCtl)))
- teCtl = (*gcteNext)(window, &teNext, nil);
- /* Find the next TextEdit control from the active control. */
-
- if (!(listCtl = (*gclNext)(window, &listNext, activeCtl)))
- listCtl = (*gclNext)(window, &listNext, nil);
- /* Find the next List control from the active control. */
-
- if ((!teNext) && (!listNext)) return(0);
- /* No TextEdit or List controls in window, so we are done. */
-
- if (!activeCtl)
- nextCtl = ((WindowPeek)window)->controlList;
- else
- nextCtl = (*activeCtl)->nextControl;
- /* At this point we probably have the following information:
- ** 1) Active control.
- ** 2) First TextEdit control after the active control.
- ** 3) First List control after the active control.
- ** What we want to do is determine if the TextEdit control or
- ** the List control is closest to the active control.
- ** We may not have a currently active control, so in that case
- ** we will start at the beginning of the window control list. */
-
- for (;;) {
- if (!nextCtl)
- nextCtl = ((WindowPeek)window)->controlList;
-
- if (nextCtl == teCtl) { /* Activate a TextEdit control... */
- BeginContent(window); /* Clip out window scrollbars and growIcon. */
- if (list)
- (*gclActivate)(false, list);
- (*gcteActivate)(true, teNext);
- teData = (CTEDataHndl)(*teCtl)->contrlData;
- if ((*teData)->mode & cteTabSelectAll)
- (*gcteSetSelect)(0, (*teNext)->teLength, teNext);
- /* If the "select all TextEdit text when tabbed into" bit is
- ** set, then do that very thing. */
- EndContent(window); /* Remove the clipping. */
- if (retCtl)
- *retCtl = teCtl;
- if (retAction)
- *retAction = action;
- return(Ctl2CNum(teCtl));
- }
-
- if (nextCtl == listCtl) {
- BeginContent(window); /* Clip out window scrollbars and growIcon. */
- if (te)
- (*gcteActivate)(false, te);
- (*gclActivate)(true, listNext);
- EndContent(window); /* Remove the clipping. */
- if (retCtl)
- *retCtl = listCtl;
- if (retAction)
- *retAction = action;
- return(Ctl2CNum(listCtl));
- }
- nextCtl = (*nextCtl)->nextControl;
- }
- }
-
- if (te = (*gcteFindActive)(window)) { /* If TextEdit control is active... */
- BeginContent(window);
- (*gcteKey)(window, event); /* Allow key to be processed by the TextEdit control. */
- action = 1;
- EndContent(window);
- ctl = (*gcteViewFromTE)(te);
- if (retCtl)
- *retCtl = ctl;
- if (retAction)
- *retAction = action;
- return(Ctl2CNum(ctl));
- }
-
- if (list = (*gclFindActive)(window)) { /* If List control is active... */
- BeginContent(window);
- (*gclKey)(window, event);
- EndContent(window);
- ctl = (*gclViewFromList)(list);
- if (retCtl)
- *retCtl = ctl;
- if (retAction)
- *retAction = action;
- return(Ctl2CNum(ctl));
- }
- }
-
- return(0);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- #pragma segment Controls
- static short HandleScrollEvent(WindowPtr window, EventRecord *event)
- {
- WindowPtr oldPort;
- Point clickLoc;
- short part;
- ControlHandle ctl;
- short value, h, v;
- RgnHandle updateRgn;
-
- GetPort(&oldPort);
- SetPort(window);
- gScrollRct = window->portRect;
-
- gKeepOrg.h = gScrollRct.left;
- gKeepOrg.v = gScrollRct.top;
-
- SetOrigin(0, -16384);
- clickLoc = event->where;
- GlobalToLocal(&clickLoc);
- /* Scrollbars for window are offset -16384 vertically. Get a local
- ** coordinate that corresponds to this negative space. */
-
- if (!(part = FindControl(clickLoc, window, &ctl))) {
- SetOrigin(gKeepOrg.h, gKeepOrg.v); /* Restore the origin. */
- SetPort(oldPort);
- return(kScrollEvent);
- }
-
- gFrHndl = (FileRecHndl)GetWRefCon(window);
- if ((*gFrHndl)->fileState.vScroll)
- gScrollRct.right -= 15;
- if ((*gFrHndl)->fileState.hScroll)
- gScrollRct.bottom -= 15;
-
- gScrollRct.left += (*gFrHndl)->fileState.leftSidebar;
- gScrollRct.top += (*gFrHndl)->fileState.topSidebar;
-
- gVert = (((*ctl)->contrlRect.right - (*ctl)->contrlRect.left) == 16);
- switch (part) {
- case inThumb:
- value = GetCtlValue(ctl);
- SetOrigin(0, -16384);
- part = TrackControl(ctl, clickLoc, nil);
- SetOrigin(gKeepOrg.h, gKeepOrg.v); /* Restore the origin. */
- if (part) {
- value -= GetCtlValue(ctl);
- /* Value now has CHANGE in position. if position changed, scroll. */
- if (value) {
- h = v = 0;
- if (gVert)
- v = value;
- else
- h = value;
- ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
- InvalRgn(updateRgn);
- DisposeRgn(updateRgn);
- DoScrollFrame(window, h, v);
- }
- }
- break;
- default:
- SetOrigin(0, -16384);
- TrackControl(ctl, clickLoc, (ProcPtr)ScrollActionProc);
- SetOrigin(gKeepOrg.h, gKeepOrg.v); /* Restore the origin. */
- break;
- }
-
- AdjustScrollBars(window);
-
- SetPort(oldPort);
- return(kScrollEvent);
- }
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- #pragma segment Controls
- static pascal void ScrollActionProc(ControlHandle scrollCtl, short part)
- {
- WindowPtr window;
- short delta, value, h, v;
- short oldValue, max;
- Point org;
- RgnHandle updateRgn, contPart, framePart;
-
- Rect r;
- SetRect(&r, -16000, -16000, 16000, 16000);
-
- GetPort(&window);
-
- if (part) { /* If it was actually in the control. */
-
- switch (part) {
- case inUpButton:
- case inDownButton: /* One line. */
- delta = (gVert) ? (*gFrHndl)->fileState.vArrowVal : (*gFrHndl)->fileState.hArrowVal;
- break;
- case inPageUp: /* One page. */
- case inPageDown:
- delta = (gVert) ? (*gFrHndl)->fileState.vPageVal : (*gFrHndl)->fileState.hPageVal;
- break;
- }
-
- if ( (part == inUpButton) || (part == inPageUp) )
- delta = -delta; /* Reverse direction for an upper. */
-
- value = (oldValue = GetCtlValue(scrollCtl)) + delta;
- if (value < 0)
- value = 0;
- if (value > (max = GetCtlMax(scrollCtl)))
- value = max;
-
- if (value != oldValue) {
-
- SetCtlValue(scrollCtl, value);
- SetOrigin(gKeepOrg.h, gKeepOrg.v);
- h = oldValue - value;
- v = 0;
- if (gVert) {
- v = h;
- h = 0;
- }
-
- ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
- InvalRgn(updateRgn);
- DisposeRgn(updateRgn);
- DoScrollFrame(window, h, v);
-
- DoUpdateSeparate(window, &contPart, &framePart);
- if (contPart) {
- CopyRgn(contPart, ((WindowPeek)window)->updateRgn);
- DisposeRgn(contPart);
- ++gBeginUpdateNested;
- BeginUpdate(window);
- GetContentOrigin(window, &org);
- SetOrigin(org.h, org.v);
- DoImageDocument(gFrHndl);
- EndUpdate(window);
- --gBeginUpdateNested;
- }
- if (framePart) {
- CopyRgn(framePart, ((WindowPeek)window)->updateRgn);
- DisposeRgn(framePart);
- }
-
- SetOrigin(0, -16384);
- }
- }
- }
-
-
-
-